/**
 * @file 06-decorators.ts
 *
 * 1.3.7 使用装饰器进行 AOP
 *
 * 使用一个角色权限验证的样例说明
 *  - 给方法的装饰器
 *  - 给类的装饰器
 */

// 模拟的当前用户信息
let currentUser = { user: "peter", roles: [{ role: "user" }, { role: "admin" }] };

/**
 * 装饰器样例接口
 * 只有 Admin可以执行的方法
 * 和 所有都可以执行的方法
 */
interface IDecoratorExample {
    AnyoneCanRun(args: string): void;
    AdminOnly(args: string): void;
}

/**
 * 没有角色权限检查时的情况
 */
class NoRoleCheck implements IDecoratorExample {
    AnyoneCanRun(args: string): void {
        console.log(args);
    }
    AdminOnly(args: string): void {
        console.log(args);
    }
}

//#region  引入权限判断

/**
 * 判断当前用户 是否存在指定角色
 * @param role 角色
 */
function IsInRole(role: string): boolean {
    return currentUser.roles.some((r) => r.role === role);
}

/**
 * 有角色检查时的情况
 * 但是 重复代码过多
 */
class RoleCheck implements IDecoratorExample {
    AnyoneCanRun(args: string): void {
        if (!IsInRole("user")) {
            console.log(`${currentUser.user} is not in the user role`);
            return;
        }
        console.log(args);
    }
    AdminOnly(args: string): void {
        if (!IsInRole("admin")) {
            console.log(`${currentUser.user} is not in the admin role`);
            return;
        }
        console.log(args);
    }
}
//#endregion

//#region 引入装饰器
/**
 * Admin 装饰器
 * @param target 我们把装饰器应用到的元素
 * @param propertyKey 该元素的名称
 * @param descriptor 应用装饰器的方法描述符
 */
function Admin(
    target: any,
    propertyKey: string | symbol,
    descriptor: PropertyDescriptor
) {
    // descriptor.value 是 被修饰的原方法
    let originalMethod = descriptor.value;

    // 修改被修饰原方法 的 部分实现方式
    descriptor.value = function () {
        if (IsInRole("admin")) {
            originalMethod.apply(this, arguments);
            return;
        }
        console.log(`${currentUser.user} is not in the admin role`);
    };
    // 返回修改后的方法
    return descriptor;
}

function Role(role: string) {
    return function (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor) {
        let originalMethod = descriptor.value;
        descriptor.value = function () {
            if (IsInRole(role)) {
                originalMethod.apply(this, arguments);
            } else {
                console.log(`${currentUser.user} is not in the ${role} role`);
            }
        }
        return descriptor;
    }
}

/**
 * 没有角色权限检查时的情况
 */
class DecoratedExampleMethodDecoration implements IDecoratorExample {
    @Role("user")
    AnyoneCanRun(args: string): void {
        console.log(args);
    }

    @Role("admin")
    AdminOnly(args: string): void {
        console.log(args);
    }
}
//#endregion

//#region 类装饰器
let classUser = { user: "peter", roles: [{ role: "user" }, { role: "admi1n" }] };

function IsInRoleClass(role: string): boolean {
    return classUser.roles.some(r => r.role === role);
}

function RoleClass(role: string) {
    return function (constructor: Function) {
        if (!IsInRoleClass(role)) {
            throw new Error(`The user is not authorised to access this class`);
        }
    }
}

@RoleClass("admin")
class RestrictedClass {
    constructor() {
        console.log(`Inside the constructor`);
    }
    Validate() {
        console.log(`Validating`);
    }
}
//#endregion

//#region  样例测试
function TestDecoratorExample(decoratorMethod: IDecoratorExample) {
    console.log(`Current user ${currentUser.user}`);
    decoratorMethod.AnyoneCanRun(`Running as user`);
    decoratorMethod.AdminOnly(`Running as admin`);
}

TestDecoratorExample(new NoRoleCheck());
console.log("----------------------");
TestDecoratorExample(new RoleCheck());
console.log("----------------------");
TestDecoratorExample(new DecoratedExampleMethodDecoration());
console.log("测试类装饰器")
let r = new RestrictedClass()
r.Validate();
currentUser = { user: "bob", roles: [{ role: "user" }] };
TestDecoratorExample(new DecoratedExampleMethodDecoration());
console.log("测试类装饰器")
r = new RestrictedClass()
r.Validate();

//#endregion